using System;
using System.Collections.Generic;
using System.Text;
using Dorm.Helpers;
using Dorm.Maps;

namespace Dorm.QueryObject
{
    public class Update
    {
        protected string EntityName { get; set; }
        protected List<Parameter> SetValues { get; set; }
        protected List<FilterExpression> Filters { get; set; }
        protected string WhereClause { get; set; }

        public Update(string entityName)
        {
            EntityName = entityName;
        }

        public Update Set(string parameterName, object parameterValue)
        {
            SetValues.Add(new Parameter { PropertyName = parameterName, PropertyValue = parameterValue});
            return this;
        }

        public Update Set(object entity)
        {
            var entityInfo = new EntityInfo(entity);
            var entityMaps = MapLocator.GetMap(entity.GetType());
            SetValues = new List<Parameter>();

            foreach (var mappedProperty in entityMaps)
            {
                if (mappedProperty.MappingType == MappedProperty.MappingTypes.Simple)
                {
                    object propertyValue = entityInfo.GetValue(mappedProperty.PropertyName);

                    if (propertyValue == null)
                    {
                        SetValues.Add(new Parameter
                        {
                            PropertyName = mappedProperty.PropertyName,
                            PropertyValue = null
                        });
                        continue;
                    }

                    if (mappedProperty.PropertyType.IsEnum)
                        propertyValue = Convert.ToInt32(propertyValue);

                    SetValues.Add(new Parameter
                    {
                        PropertyName = mappedProperty.PropertyName,
                        PropertyValue = propertyValue
                    });
                }
                else if (mappedProperty.MappingType == MappedProperty.MappingTypes.Reference)
                {
                    var referenceValue = entityInfo.GetValue(mappedProperty.PropertyName);

                    if (referenceValue == null)
                    {
                        SetValues.Add(new Parameter
                        {
                            PropertyName = mappedProperty.PropertyName + "ID",
                            PropertyValue = null
                        });
                    }
                    else
                    {
                        var referenceInfo = new EntityInfo(referenceValue);

                        SetValues.Add(new Parameter
                        {
                            PropertyName = mappedProperty.PropertyName + "ID",
                            PropertyValue = referenceInfo.ID
                        });
                    }
                }
                else if (mappedProperty.MappingType == MappedProperty.MappingTypes.Component)
                {
                    var componentValueObject = entityInfo.GetValue(mappedProperty.PropertyName);
                    if (componentValueObject == null)
                        continue;

                    var componentInfo = new EntityInfo(componentValueObject);

                    var componentProperties = MapLocator.GetMap(componentInfo.EntityType);

                    foreach (var componentProperty in componentProperties)
                    {
                        if (componentProperty.MappingType == MappedProperty.MappingTypes.Simple)
                        {
                            var propertyValue = componentInfo.GetValue(componentProperty.PropertyName);

                            if (propertyValue == null)
                            {
                                SetValues.Add(new Parameter
                                {
                                    PropertyName = mappedProperty.PropertyName + componentProperty.PropertyName,
                                    PropertyValue = null
                                });
                                continue;
                            }

                            SetValues.Add(new Parameter
                            {
                                PropertyName = mappedProperty.PropertyName + componentProperty.PropertyName,
                                PropertyValue = componentInfo.GetValue(componentProperty.PropertyName)
                            });
                        }
                        else if (componentProperty.MappingType == MappedProperty.MappingTypes.Reference)
                        {
                            var componentValue = componentInfo.GetValue(componentProperty.PropertyName);

                            if (componentValue == null)
                            {
                                SetValues.Add(new Parameter
                                {
                                    PropertyName = mappedProperty.PropertyName + componentProperty.PropertyName + "ID",
                                    PropertyValue = null
                                });
                            }
                            else
                            {
                                var propertyValue = componentProperty.PropertyType.GetProperty("ID")
                                .GetValue(componentValue, null);

                                SetValues.Add(new Parameter
                                {
                                    PropertyName = mappedProperty.PropertyName + componentProperty.PropertyName + "ID",
                                    PropertyValue = propertyValue
                                });
                            }
                        }
                    }
                }
            }

            Filter("ID", FilterOptions.Equals, entityInfo.ID);

            return this;
        }

        public Update Filter(string propertyName, FilterOptions comparison, object propertyValue)
        {
            if (Filters == null) Filters = new List<FilterExpression>();
            var filter = new FilterExpression
                             {
                                 PropertyName = propertyName,
                                 Comparison = comparison,
                                 CompareValue = propertyValue
                             };
            Filters.Add(filter);
            return this;
        }

        public Update Where(string whereClause)
        {
            WhereClause = whereClause;
            return this;
        }

        public override string ToString()
        {
            var expression = new StringBuilder();

            expression.AppendLine("UPDATE " + EntityName + " SET");

            if (SetValues != null)
            {
                expression.AppendLine("  " + SetValues[0].PropertyName + " = " + SetValues[0].PropertyValue);

                if (SetValues.Count > 1)
                {
                    for (int i = 1; i < SetValues.Count; i++)
                    {
                        expression.AppendLine(" ," + SetValues[i].PropertyName + " = " + SetValues[i].PropertyValue);
                    }
                }
            }

            if (Filters != null)
            {
                expression.AppendLine("WHERE");
                expression.Append("      ");
           
                expression.AppendLine(Filters[0].ToString());

                if (Filters.Count > 1)
                {
                    for (int f = 1; f < Filters.Count; f++)
                    {
                        var concatenator = Filters[f].Concatenate ?? "AND";
                        expression.Append("  " + concatenator + " ");

                        expression.AppendLine(Filters[f].ToString());
                    }
                }
            }
            else if (WhereClause != null)
            {
                expression.AppendLine("WHERE");
                expression.AppendLine(WhereClause);
            }

            return expression.ToString();
        }

    }
}
